#include <linux/init.h>
#include <linux/module.h>

#define MPU6500_MINIORS		32 

static LIST_HEAD (device_list);
static DEFINE_MUTEX (device_list_lock);
static DECLARE_BITMAP (minors, MPU6500_MINIORS);
static int major = 0;
static struct class *mpu6500_cls  = NULL;
static int mpu6500_probe (struct spi_device *spi);
static int mpu6500_remove (struct spi_device *spi);

static const struct of_device_id mpu6500_dt_ids[] = {
	{.compatile = "InvenSense,mpu6500"},
	{}
};
static struct spi_driver mpu6500_spi_driver = {
	.driver = {
		.name  = "mpu6500",
		.of_match_table = of_match_ptr (mpu6500_dt_ids),
	},
	.probe = mpu6500_probe,
	.remove = mpu6500_remove,
};

struct mpu6500_data {
	dev_t				dev;
	spinlock_t			spi_lock;//自旋锁
	struct spi_device	*spi;
	struct list_head	device_entry;

	//miso mosi
	struct mutex	buf_lock;
	unsigned char	*tx_buffer;
	unsigned char	*rx_buffer;
	unsigned int	speed_hz
};


static int mpu6500_probe (struct spi_device *spi)
{
	struct resource  *maddr;
	void __iomem     *vaddr;
	struct mpu6500_data *spidev;
	unsigned long    minor;
	int				 status;

	//申请io内存
	maddr = request_mem_region (0x32b300e8, 40, " ");
	vaddr = ioremap (maddr->start, 16);
	
	//将gpio管脚复用为spi功能
	writel (0x40, vaddr);
	writel (0x40, vaddr + 4);
	writel (0x40, vaddr + 8);
	writel (0x40, vaddr + 12);
	
	//初始化spi的配置 
	spidev = kzalloc (sizeof(*spidev), GFP_KERNEL);
	if (!spidev) {
		return -ENOMEM;
	}
	spidev->spi = spi;
	
	spin_lock_init (&spidev->spi_lock);
	mutex_init (&spidev->buf_lock);
	INIT_LIST_HEAD(&spidev->device_entry);

	mutex_lock (&device_list_lock);
	minor = find_first_zero_bit (minors, MPU6500_MINIORS);
	if (minor < MPU6500_MINIORS) {
		struct device *dev;
		spidev->dev =  MKDEV (major, minor);
		//创建设备文件 
		dev = device_create (mpu6500_cls, &spi->dev, spidev->dev, "mpu6500.%d.%d", 
				spi->master->bus_num, spi->chip_select);
		status =  PTR_ERR_OR_ZERO (dev);
	} else {
		status = -ENODEV;
	}
	if (0 == status) {
		set_bit (minor, minors);
		list_add (&spidev->device_entry, &device_list);
	}
	mutex_unlock (&device_list_lock);
	
	spidev->speed_hz = spi->max_speed_hz;
	
	if (status == 0) {
		spi_set_drvdata (spi, spidev);
	} else {
		kfree (spidev);
	}
	return status;
}

static int mpu6500_remove (struct spi_device *spi)
{
	struct mpu6500_data *spidev =  spi_get_drvdata (spi);
	release_mem_region (0x32b300e8, 40);

	spin_lock_irq (&spidev->spi_lock);
	spidev->spi = NULL;
	//usleep (20);
	spin_unlock_irq (&spidev->spi_lock);
	
	list_del (&spidev->device_entry);
	device_destroy (mpu6500_cls, spidev->dev);
	clear_bit (MINOR (spidev->dev), minors);
	kfree (spidev);
	mutex_unlock (&device_list_lock);
	return 0;
}

static int __init mpu6500_init (void)
{
	int status;
	//注册设备
	major = register_chrdev (0, "spi", &mpu6500_fops);
	//设备类的自动创建
	mpu6500_cls = class_create (THIS_MODULE, "mpu6500");

	//注册设备驱动程序
	status = spi_register_driver (&mpu6500_spi_driver);
	return status;
}

static void __exit mpu6500_exit (void)
{
	spi_unregister_driver (&mpu6500_spi_driver);
	//注销设备类
	class_destroy (mpu6500_cls);
	//注销cdev对象
	unregister_chrdev (major, "spi");
	return ;
}

module_init (mpu6500_init);
module_exit (mpu6500_exit);
MODULE_LICENSE ("GPL");
